﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml.Linq;
using Acando.Dynamics.CRM.Silverlight.Library.Controls;
using Acando.Dynamics.CRM.Silverlight.Library.CrmSdk;
using Acando.Dynamics.CRM.Silverlight.Library.Helper;
using Acando.Dynamics.CRM.Silverlight.Library.Interfaces;
using Acando.Dynamics.CRM.Silverlight.Library.MetaSdk;
using Acando.Dynamics.CRM.Silverlight.Library.Model;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Commands;
using RiaCrm.DataAccessLayer;
using RiaCrm.Infrastructure;

namespace RiaCrm.DynamicEntityGrid
{
    public class DynamicEntityGridViewModel : INotifyPropertyChanged, IProviedMetaDataLayer
    {
        private EntityMetadata _entityMetadata;
        internal Dictionary<object, IRecordControl> _recordControls;
        private String _layoutxml;
        private String _entityTypeName;
        private IDataModel _dataModel;  // ist eigentlich eine FacadeLayer und keine DataModel
        private IEnumerable _items;     // gehört u.U. ins Model
        private object _currentItem;    // gehört u.U. ins Model
        private IEventAggregator _eventAggregator;

        public IMetaDataLayer Metadatalayer { get; set; }

        # region Properties

        public String Organame { get; private set; }

        public object CurrentItem
        {
            get { return this._currentItem; }
            set { 
                this._currentItem = value;
                this.OnCurrentItemChanged(value);
            }
        }

        public String LayoutXml
        {
            get { return this._layoutxml; }
            set
            {
                this._layoutxml = value;
                OnPropertyChanged("LayoutXml");
            }
        }
        
        public EntityMetadata EntityMetadata
        {
            get { return this._entityMetadata; }
            //set { this.EntityMetadata = value; }
            set { this._entityMetadata = value; }
        }
        
        public String EntityTypeName
        {
            get { return this._entityTypeName; }
            set 
            { 
                this._entityTypeName = value;
                EntityTypeNameChanged(this._entityTypeName);
            }
        }
        
        public IEnumerable Items
        {
            get { return this._items; }
            set
            {
                this._items = value;
                OnPropertyChanged("Items");
            }
        }

        # endregion

        # region Events

        public event EventHandler<EventArgs> LoadingItemsStart;
        public event EventHandler<EventArgs> LoadingItemsCompleted;

        # endregion

        // The parameter of this class are all provided by the container!
        public DynamicEntityGridViewModel(IMetaDataLayer metadatalayer, IEventAggregator eventAggregator, InitParaEntityname entitylogicalname, InitParaOrgaName organame)
        {
            this.Organame = organame.Value;
            this._recordControls = new Dictionary<object, IRecordControl>();
            this.Metadatalayer = metadatalayer;

            this._eventAggregator = eventAggregator;
            this._eventAggregator.GetEvent<DataSourceChanged>().Subscribe(OnDataSourceChanged);
            this._eventAggregator.GetEvent<StatedEntityChanged>().Subscribe(OnStatedEntityChanged);
            this._eventAggregator.GetEvent<SearchEvent>().Subscribe(OnSearchEvent);
            
            this.EntityTypeName = entitylogicalname.Value;

            this._dataModel= new DynamicDataModel( this.EntityTypeName, this.Organame );
            
        }

        public void OnDataSourceChanged(string payload)
        {
            // the payload is just a dummy value, only the event itself is relevant
            this.LoadItems();
        }

        public void OnSearchEvent(string searchByStr)
        {
            if (!String.IsNullOrEmpty(searchByStr))
            {
                XDocument xmlTree = XDocument.Parse(this.LayoutXml);

                QueryExpression query = new QueryExpression();
                FilterExpression filter = new FilterExpression();
                filter.FilterOperator = LogicalOperator.Or;

                List<ConditionExpression> condtionCollection = new List<ConditionExpression>();
                // searching in linked entities is currently not supported
                foreach (var cell in xmlTree.Element("grid").Element("row").Elements("cell").Where(c => !c.Attribute("name").Value.Contains(".")))
                {


                    String columnName = cell.Attribute("name").Value;

                    AttributeMetadata attMeta = this.EntityMetadata.GetAttributeMetadata(columnName);
                    CrmAttributeType attType = attMeta.AttributeType;

                    // use only String types for searching
                    if (attType.Value.ToString().ToLower() == "string")
                    {

                        ConditionExpression condition = new ConditionExpression();
                        condition.Operator = ConditionOperator.Like;
                        condition.AttributeName = columnName;
                        condition.Values = new String[] { "%"+searchByStr+"%" };

                        condtionCollection.Add(condition);
                    }
                }

                filter.Conditions = condtionCollection.ToArray();

                query.EntityName = this.EntityTypeName;
                query.ColumnSet = new AllColumns();
                query.Criteria = filter;

                LoadItems(query);
            }
            else
            {
                this.LoadItems();
            }
        }


        public void OnStatedEntityChanged(string newEntityTypename)
        {
            if (!String.IsNullOrEmpty(this.EntityTypeName) && this.EntityTypeName.ToLower().Trim() != newEntityTypename.ToLower().Trim())
            {
                this.EntityTypeName = newEntityTypename;
                this._dataModel = new DynamicDataModel(this.EntityTypeName, this.Organame);
                this.LoadItems();
            }
        }

        private void OnCurrentItemChanged(object dynamicEntity)
        {
            this._eventAggregator.GetEvent<DynamicEntityGridSelecteIndexChanged>().Publish(dynamicEntity);
        }

        internal void LoadItems()
        {
            this.OnLoadingItemsStart();

            this._dataModel.RetrieveMultipleCompleted += new EventHandler<RetrieveMultipleCompletedEventArgs>( _dataModel_RetrieveMultipleCompleted );
            this._dataModel.RetrieveMultiple();
        }

        internal void LoadItems(QueryExpression query)
        {
            this.OnLoadingItemsStart();

            this._dataModel.RetrieveMultipleCompleted += new EventHandler<RetrieveMultipleCompletedEventArgs>(_dataModel_RetrieveMultipleCompleted);
            this._dataModel.RetrieveMultiple(query);
        }

        private void _dataModel_RetrieveMultipleCompleted(object sender, RetrieveMultipleCompletedEventArgs e)
        {
            if (e.Error != null)
                MessageBox.Show(e.Error.Message);
            else
            {
                this.Items = CrmServiceConversions.ConvertToCollection<DynamicEntity>(e.Result);
                //this.CurrentItem = (e.Result.BusinessEntities.Count() > 0 ? 0 : -1);
                this.OnLoadingItemsCompleted();
                
            } 
        }

        private void EntityTypeNameChanged(string newTypeName)
        {
            this.Metadatalayer.LoadMetaData(newTypeName, OnLoadMetaDataCompletedCallback);
        }

        private void OnLoadingItemsStart()
        {
            if (this.LoadingItemsStart != null)
                this.LoadingItemsStart(this, new EventArgs());
        }

        private void OnLoadingItemsCompleted()
        {
            if (this.LoadingItemsCompleted != null)
                this.LoadingItemsCompleted(this, new EventArgs());
        }

        private void PublishContextLoadedEvent()
        {
            List<String> columnsList = XDocument.Parse( this.LayoutXml).Element("grid").Element("row").Elements("cell").Select(c=> c.Attribute("name").Value).ToList<String>();
          
            this._eventAggregator.GetEvent<ContextLoaded>().Publish(new PayLoadContext(){ Columns = columnsList, MetaData = this.EntityMetadata });  
        
        }

        # region Callback methods

        private void OnLoadMetaDataCompletedCallback(EntityMetadata metadata)
        {
            this._entityMetadata = metadata;

            this.Metadatalayer.LoadLayountXmlData(this._entityMetadata, OnLoadLayountXmlCompletedCallback);
            
        }

        public void OnLoadLayountXmlCompletedCallback(String layountxml)
        {
            this.LayoutXml = layountxml;

            // Inform other modules:
            this.PublishContextLoadedEvent();
        }

        # endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}
